home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2002 #11 / Amiga Plus CD - 2002 - No. 11.iso / Tools / Development / ncurses-5.3 / ncurses / tty / lib_tstp.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-10-27  |  11.7 KB  |  400 lines

  1. /****************************************************************************
  2.  * Copyright (c) 1998-2001,2002 Free Software Foundation, Inc.              *
  3.  *                                                                          *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a  *
  5.  * copy of this software and associated documentation files (the            *
  6.  * "Software"), to deal in the Software without restriction, including      *
  7.  * without limitation the rights to use, copy, modify, merge, publish,      *
  8.  * distribute, distribute with modifications, sublicense, and/or sell       *
  9.  * copies of the Software, and to permit persons to whom the Software is    *
  10.  * furnished to do so, subject to the following conditions:                 *
  11.  *                                                                          *
  12.  * The above copyright notice and this permission notice shall be included  *
  13.  * in all copies or substantial portions of the Software.                   *
  14.  *                                                                          *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
  16.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
  17.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
  18.  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
  19.  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
  20.  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
  21.  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
  22.  *                                                                          *
  23.  * Except as contained in this notice, the name(s) of the above copyright   *
  24.  * holders shall not be used in advertising or otherwise to promote the     *
  25.  * sale, use or other dealings in this Software without prior written       *
  26.  * authorization.                                                           *
  27.  ****************************************************************************/
  28.  
  29. /****************************************************************************
  30.  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
  31.  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
  32.  *     and: Thomas Dickey 1995-2001                                         *
  33.  ****************************************************************************/
  34.  
  35. /*
  36. **    lib_tstp.c
  37. **
  38. **    The routine _nc_signal_handler().
  39. **
  40. */
  41. #include <curses.priv.h>
  42.  
  43. #include <SigAction.h>
  44.  
  45. #if SVR4_ACTION && !defined(_POSIX_SOURCE)
  46. #define _POSIX_SOURCE
  47. #endif
  48.  
  49. MODULE_ID("$Id: lib_tstp.c,v 1.30 2002/05/18 19:55:38 tom Exp $")
  50.  
  51. #if defined(SIGTSTP) && (HAVE_SIGACTION || HAVE_SIGVEC)
  52. #define USE_SIGTSTP 1
  53. #else
  54. #define USE_SIGTSTP 0
  55. #endif
  56.  
  57. #ifdef TRACE
  58. static const char *
  59. signal_name(int sig)
  60. {
  61.     switch (sig) {
  62.     case SIGALRM:
  63.     return "SIGALRM";
  64. #ifdef SIGCONT
  65.     case SIGCONT:
  66.     return "SIGCONT";
  67. #endif
  68.     case SIGINT:
  69.     return "SIGINT";
  70.     case SIGQUIT:
  71.     return "SIGQUIT";
  72.     case SIGTERM:
  73.     return "SIGTERM";
  74. #ifdef SIGTSTP
  75.     case SIGTSTP:
  76.     return "SIGTSTP";
  77. #endif
  78. #ifdef SIGTTOU
  79.     case SIGTTOU:
  80.     return "SIGTTOU";
  81. #endif
  82. #ifdef SIGWINCH
  83.     case SIGWINCH:
  84.     return "SIGWINCH";
  85. #endif
  86.     default:
  87.     return "unknown signal";
  88.     }
  89. }
  90. #endif
  91.  
  92. /*
  93.  * Note: This code is fragile!  Its problem is that different OSs
  94.  * handle restart of system calls interrupted by signals differently.
  95.  * The ncurses code needs signal-call restart to happen -- otherwise,
  96.  * interrupted wgetch() calls will return FAIL, probably making the
  97.  * application think the input stream has ended and it should
  98.  * terminate.  In particular, you know you have this problem if, when
  99.  * you suspend an ncurses-using lynx with ^Z and resume, it dies
  100.  * immediately.
  101.  *
  102.  * Default behavior of POSIX sigaction(2) is not to restart
  103.  * interrupted system calls, but Linux's sigaction does it anyway (at
  104.  * least, on and after the 1.1.47 I (esr) use).  Thus this code works
  105.  * OK under Linux.  The 4.4BSD sigaction(2) supports a (non-portable)
  106.  * SA_RESTART flag that forces the right behavior.  Thus, this code
  107.  * should work OK under BSD/OS, NetBSD, and FreeBSD (let us know if it
  108.  * does not).
  109.  *
  110.  * Stock System Vs (and anything else using a strict-POSIX
  111.  * sigaction(2) without SA_RESTART) may have a problem.  Possible
  112.  * solutions:
  113.  *
  114.  *    sigvec      restarts by default (SV_INTERRUPT flag to not restart)
  115.  *    signal      restarts by default in SVr4 (assuming you link with -lucb)
  116.  *                and BSD, but not SVr3.
  117.  *    sigset      restarts, but is only available under SVr4/Solaris.
  118.  *
  119.  * The signal(3) call is mandated by the ANSI standard, and its
  120.  * interaction with sigaction(2) is described in the POSIX standard
  121.  * (3.3.4.2, page 72,line 934).  According to section 8.1, page 191,
  122.  * however, signal(3) itself is not required by POSIX.1.  And POSIX is
  123.  * silent on whether it is required to restart signals.
  124.  *
  125.  * So.  The present situation is, we use sigaction(2) with no
  126.  * guarantee of restart anywhere but on Linux and BSD.  We could
  127.  * switch to signal(3) and collar Linux, BSD, and SVr4.  Any way
  128.  * we slice it, System V UNIXes older than SVr4 will probably lose
  129.  * (this may include XENIX).
  130.  *
  131.  * This implementation will probably be changed to use signal(3) in
  132.  * the future.  If nothing else, it's simpler...
  133.  */
  134.  
  135. #if USE_SIGTSTP
  136. static void
  137. tstp(int dummy GCC_UNUSED)
  138. {
  139.     sigset_t mask, omask;
  140.     sigaction_t act, oact;
  141.  
  142. #ifdef SIGTTOU
  143.     int sigttou_blocked;
  144. #endif
  145.  
  146.     T(("tstp() called"));
  147.  
  148.     /*
  149.      * The user may have changed the prog_mode tty bits, so save them.
  150.      *
  151.      * But first try to detect whether we still are in the foreground
  152.      * process group - if not, an interactive shell may already have
  153.      * taken ownership of the tty and modified the settings when our
  154.      * parent was stopped before us, and we would likely pick up the
  155.      * settings already modified by the shell.
  156.      */
  157.     if (SP != 0 && !SP->_endwin)    /* don't do this if we're not in curses */
  158. #if HAVE_TCGETPGRP
  159.     if (tcgetpgrp(STDIN_FILENO) == getpgrp())
  160. #endif
  161.         def_prog_mode();
  162.  
  163.     /*
  164.      * Block window change and timer signals.  The latter
  165.      * is because applications use timers to decide when
  166.      * to repaint the screen.
  167.      */
  168.     (void) sigemptyset(&mask);
  169.     (void) sigaddset(&mask, SIGALRM);
  170. #if USE_SIGWINCH
  171.     (void) sigaddset(&mask, SIGWINCH);
  172. #endif
  173.     (void) sigprocmask(SIG_BLOCK, &mask, &omask);
  174.  
  175. #ifdef SIGTTOU
  176.     sigttou_blocked = sigismember(&omask, SIGTTOU);
  177.     if (!sigttou_blocked) {
  178.     (void) sigemptyset(&mask);
  179.     (void) sigaddset(&mask, SIGTTOU);
  180.     (void) sigprocmask(SIG_BLOCK, &mask, NULL);
  181.     }
  182. #endif
  183.  
  184.     /*
  185.      * End window mode, which also resets the terminal state to the
  186.      * original (pre-curses) modes.
  187.      */
  188.     endwin();
  189.  
  190.     /* Unblock SIGTSTP. */
  191.     (void) sigemptyset(&mask);
  192.     (void) sigaddset(&mask, SIGTSTP);
  193. #ifdef SIGTTOU
  194.     if (!sigttou_blocked) {
  195.     /* Unblock this too if it wasn't blocked on entry */
  196.     (void) sigaddset(&mask, SIGTTOU);
  197.     }
  198. #endif
  199.     (void) sigprocmask(SIG_UNBLOCK, &mask, NULL);
  200.  
  201.     /* Now we want to resend SIGSTP to this process and suspend it */
  202.     act.sa_handler = SIG_DFL;
  203.     sigemptyset(&act.sa_mask);
  204.     act.sa_flags = 0;
  205. #ifdef SA_RESTART
  206.     act.sa_flags |= SA_RESTART;
  207. #endif /* SA_RESTART */
  208.     sigaction(SIGTSTP, &act, &oact);
  209.     kill(getpid(), SIGTSTP);
  210.  
  211.     /* Process gets suspended...time passes...process resumes */
  212.  
  213.     T(("SIGCONT received"));
  214.     sigaction(SIGTSTP, &oact, NULL);
  215.     flushinp();
  216.  
  217.     /*
  218.      * If the user modified the tty state while suspended, he wants
  219.      * those changes to stick.  So save the new "default" terminal state.
  220.      */
  221.     def_shell_mode();
  222.  
  223.     /*
  224.      * This relies on the fact that doupdate() will restore the
  225.      * program-mode tty state, and issue enter_ca_mode if need be.
  226.      */
  227.     doupdate();
  228.  
  229.     /* Reset the signals. */
  230.     (void) sigprocmask(SIG_SETMASK, &omask, NULL);
  231. }
  232. #endif /* USE_SIGTSTP */
  233.  
  234. static void
  235. cleanup(int sig)
  236. {
  237.     static int nested;
  238.  
  239.     /*
  240.      * Actually, doing any sort of I/O from within an signal handler is
  241.      * "unsafe".  But we'll _try_ to clean up the screen and terminal
  242.      * settings on the way out.
  243.      */
  244.     if (!nested++
  245.     && (sig == SIGINT
  246.         || sig == SIGQUIT)) {
  247. #if HAVE_SIGACTION || HAVE_SIGVEC
  248.     sigaction_t act;
  249.     sigemptyset(&act.sa_mask);
  250.     act.sa_flags = 0;
  251.     act.sa_handler = SIG_IGN;
  252.     if (sigaction(sig, &act, NULL) == 0)
  253. #else
  254.     if (signal(sig, SIG_IGN) != SIG_ERR)
  255. #endif
  256.     {
  257.         SCREEN *scan = _nc_screen_chain;
  258.         while (scan) {
  259.         if (SP != 0
  260.             && SP->_ofp != 0
  261.             && isatty(fileno(SP->_ofp))) {
  262.             SP->_cleanup = TRUE;
  263.             SP->_outch = _nc_outch;
  264.         }
  265.         set_term(scan);
  266.         endwin();
  267.         if (SP)
  268.             SP->_endwin = FALSE;    /* in case we have an atexit! */
  269.         scan = scan->_next_screen;
  270.         }
  271.     }
  272.     }
  273.     exit(EXIT_FAILURE);
  274. }
  275.  
  276. #if USE_SIGWINCH
  277. static void
  278. sigwinch(int sig GCC_UNUSED)
  279. {
  280.     SCREEN *scan = _nc_screen_chain;
  281.     while (scan) {
  282.     scan->_sig_winch = TRUE;
  283.     scan = scan->_next_screen;
  284.     }
  285. }
  286. #endif /* USE_SIGWINCH */
  287.  
  288. /*
  289.  * If the given signal is still in its default state, set it to the given
  290.  * handler.
  291.  */
  292. static int
  293. CatchIfDefault(int sig, RETSIGTYPE (*handler) (int))
  294. {
  295.     int result;
  296. #if HAVE_SIGACTION || HAVE_SIGVEC
  297.     sigaction_t old_act;
  298.     sigaction_t new_act;
  299.  
  300.     memset(&new_act, 0, sizeof(new_act));
  301.     sigemptyset(&new_act.sa_mask);
  302. #ifdef SA_RESTART
  303. #ifdef SIGWINCH
  304.     if (sig != SIGWINCH)
  305. #endif
  306.     new_act.sa_flags |= SA_RESTART;
  307. #endif /* SA_RESTART */
  308.     new_act.sa_handler = handler;
  309.  
  310.     if (sigaction(sig, NULL, &old_act) == 0
  311.     && (old_act.sa_handler == SIG_DFL
  312.         || old_act.sa_handler == handler
  313. #if USE_SIGWINCH
  314.         || (sig == SIGWINCH && old_act.sa_handler == SIG_IGN)
  315. #endif
  316.     )) {
  317.     (void) sigaction(sig, &new_act, NULL);
  318.     result = TRUE;
  319.     } else {
  320.     result = FALSE;
  321.     }
  322. #else /* !HAVE_SIGACTION */
  323.     RETSIGTYPE (*ohandler) (int);
  324.  
  325.     ohandler = signal(sig, SIG_IGN);
  326.     if (ohandler == SIG_DFL
  327.     || ohandler == handler
  328. #if USE_SIGWINCH
  329.     || (sig == SIGWINCH && ohandler == SIG_IGN)
  330. #endif
  331.     ) {
  332.     signal(sig, handler);
  333.     result = TRUE;
  334.     } else {
  335.     signal(sig, ohandler);
  336.     result = FALSE;
  337.     }
  338. #endif
  339.     T(("CatchIfDefault - will %scatch %s",
  340.        result ? "" : "not ", signal_name(sig)));
  341.     return result;
  342. }
  343.  
  344. /*
  345.  * This is invoked once at the beginning (e.g., from 'initscr()'), to
  346.  * initialize the signal catchers, and thereafter when spawning a shell (and
  347.  * returning) to disable/enable the SIGTSTP (i.e., ^Z) catcher.
  348.  *
  349.  * If the application has already set one of the signals, we'll not modify it
  350.  * (during initialization).
  351.  *
  352.  * The XSI document implies that we shouldn't keep the SIGTSTP handler if
  353.  * the caller later changes its mind, but that doesn't seem correct.
  354.  */
  355. NCURSES_EXPORT(void)
  356. _nc_signal_handler(bool enable)
  357. {
  358.     static bool initialized = FALSE;
  359.  
  360.     T((T_CALLED("_nc_signal_handler(%d)"), enable));
  361. #if USE_SIGTSTP            /* Xenix 2.x doesn't have SIGTSTP, for example */
  362.     {
  363.     static bool ignore_tstp = FALSE;
  364.  
  365.     if (!ignore_tstp) {
  366.         static sigaction_t act, oact;
  367.  
  368.         if (!enable) {
  369.         act.sa_handler = SIG_IGN;
  370.         sigaction(SIGTSTP, &act, &oact);
  371.         } else if (act.sa_handler != SIG_DFL) {
  372.         sigaction(SIGTSTP, &oact, NULL);
  373.         } else if (sigaction(SIGTSTP, NULL, &oact) == 0
  374.                && (oact.sa_handler == SIG_DFL)) {
  375.         sigemptyset(&act.sa_mask);
  376. #ifdef SA_RESTART
  377.         act.sa_flags |= SA_RESTART;
  378. #endif /* SA_RESTART */
  379.         act.sa_handler = tstp;
  380.         (void) sigaction(SIGTSTP, &act, NULL);
  381.         } else {
  382.         ignore_tstp = TRUE;
  383.         }
  384.     }
  385.     }
  386. #endif /* !USE_SIGTSTP */
  387.  
  388.     if (!initialized) {
  389.     if (enable) {
  390.         CatchIfDefault(SIGINT, cleanup);
  391.         CatchIfDefault(SIGTERM, cleanup);
  392. #if USE_SIGWINCH
  393.         CatchIfDefault(SIGWINCH, sigwinch);
  394. #endif
  395.         initialized = TRUE;
  396.     }
  397.     }
  398.     returnVoid;
  399. }
  400.